﻿using System;
using System.Collections.Generic;
using System.Linq;

using Microsoft.SqlServer.Management.SqlParser.SqlCodeDom;

namespace iTool.SQL.AIHandle
{
    public abstract class SqlAnalysisBase
    {
        protected SqlCodeObject CodeObject;
        public SqlAnalysisContext Context { get; set; }

        public SqlAnalysisBase(SqlAnalysisContext context)
        {
            this.Context = context;
        }


        public abstract void Analysis();

        protected virtual void Next(
            Func<int, SqlCodeObject, SqlAnalysisBase, bool>? beforeAction = null, 
            Action<int, SqlCodeObject, SqlAnalysisBase>? afterAction = null,
            object whereProperty = null)
        {
            if (this.CodeObject?.Children != null)
            {

                if (string.IsNullOrWhiteSpace(this.Context.WhereCase))
                {
                    var where = this.CodeObject.Children.OfType<SqlWhereClause>().FirstOrDefault();
                    if (where != null)
                    {
                        this.Context.WhereCase = where.Sql;
                    }
                }

                //if (this.Context.SortByOptions == null)
                //{
                //    var order = this.CodeObject.Children.OfType<SqlOrderByClause>().FirstOrDefault();
                //    if (order != null)
                //    {
                //        this.Context.SortByOptions = new SortByOptions 
                //        {
                //            OrderCase= order.Sql,
                //            SortOrder = order.Children.OfType<SqlOrderByItem>().First().SortOrder
                //        };
                //    }
                //}


                int index = 0;
                var froms = this.CodeObject.Children.OfType<SqlFromClause>()?.ToList();
                if (froms != null)
                {
                    foreach (var item in froms)
                    {
                        SqlAnalysisBase sqlAnalysis = SqlAnalysisFactory.GetNextSqlAnalysis(item.GetType(), this.Context);
                        if (sqlAnalysis?.Validation(this.CodeObject) == true)
                        {
                            if (beforeAction == null || beforeAction?.Invoke(index, item, sqlAnalysis) == true)
                            {
                                sqlAnalysis.SetCodeObject(item);
                                sqlAnalysis.Analysis();
                            }
                            afterAction?.Invoke(index, item, sqlAnalysis);
                        }
                        index++;
                    }
                }

                foreach (var item in this.CodeObject.Children)
                {
                    if (item.GetType() == typeof(SqlFromClause)) continue;

                    SqlAnalysisBase sqlAnalysis = SqlAnalysisFactory.GetNextSqlAnalysis(item.GetType(),this.Context);
                    if (sqlAnalysis?.Validation(this.CodeObject) == true)
                    {
                        if (beforeAction == null || beforeAction?.Invoke(index, item, sqlAnalysis) == true)
                        {
                            sqlAnalysis.SetCodeObject(item);
                            sqlAnalysis.Analysis();
                        }
                        afterAction?.Invoke(index,item, sqlAnalysis);
                    }
                    index++;
                }
            }
        }

        private void SetCodeObject(SqlCodeObject codeObject)
        {
            this.CodeObject = codeObject;
        }

        protected bool Validation<Tin>(SqlCodeObject codeObject)
            where Tin : SqlCodeObject
        {
            this.CodeObject = codeObject.Children.OfType<Tin>().FirstOrDefault();
            return this.CodeObject != null;
        }

        protected void DebugLogger<T>(string message)
        {
        }
        protected void DebugLogger<T>(Exception ex)
        {
        }

        public abstract bool Validation(SqlCodeObject codeObject);


        public Dictionary<string, LinkedList<string>> GetTableStructure(out string error) 
        {
            error = string.Empty;

            int tableIndex = 0, tableIndex2 = 0;
            bool hasAdd2 = false,first0 = true;
            Dictionary<string, LinkedList<string>> keyValuePairs = new Dictionary<string, LinkedList<string>>();
            for (int i = 0; i < this.Context.Fields.Count; i++)
            {
                try
                {
                    var field = this.Context.Fields[i];
                    if (field == null)
                    {
                        if (tableIndex == 0)
                        {
                            if (first0) first0 = false;
                            else
                            {
                                tableIndex += tableIndex2;
                                hasAdd2 = true;
                            }
                        }
                        tableIndex++;
                        continue;
                    }
                    else if (field.Value.Key == null && field.Value.Key == null)
                    {
                        tableIndex--;
                        if (hasAdd2)
                        {
                            tableIndex -= tableIndex2;
                        }
                        if (tableIndex < 0)
                        {
                            tableIndex = 0;
                            //throw new Exception("字段解析错误，where 不支持嵌套子查询");
                        }
                        tableIndex2++;
                        continue;
                    }
                    else
                    {
                        string tableName = string.IsNullOrWhiteSpace(field.Value.Key) ?
                            this.Context.Tables[tableIndex].Value
                            : this.Context.Tables.Find(item => item.Key == field.Value.Key || item.Value == field.Value.Key).Value;

                        if (tableName == null)
                        {
                            error = "Undefined: field alias is " + field.Value.Key;
                        }

                        //tableName = tableName.ToUpper();

                        if (!keyValuePairs.ContainsKey(tableName))
                        {
                            keyValuePairs.Add(tableName, new LinkedList<string>());
                        }

                        if (!keyValuePairs[tableName].Contains(field.Value.Value.ToUpper()))
                        {
                            keyValuePairs[tableName].AddLast(field.Value.Value.ToUpper());
                        }
                    }
                }
                catch (Exception)
                {
                    throw;
                }
            }

            if (!keyValuePairs.Any())
            {
                keyValuePairs.Add(this.Context.Tables[0].Value, new LinkedList<string>());
            }

            return keyValuePairs;
        }
    }
}
